using System;
using System.Collections.Generic;
using CodePlex.TfsLibrary.ObjectModel;
using Xunit;

namespace CodePlex.TfsLibrary.ClientEngine
{
    public class TfsEngineDiffTest
    {
        public class Diff
        {
            [Fact]
            public void NullDelegateThrows()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         engine.Diff(@"C:\Foo", false, null);
                                                     });
            }

            [Fact]
            public void NullPathThrows()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         engine.Diff(null, true, delegate {});
                                                     });
            }

            [Fact]
            public void PathNotFound()
            {
                string callbackLeftPathname = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();

                engine.Diff(@"C:\Foo", false, delegate(string leftPathname,
                                                       string leftVersion,
                                                       string rightPathname,
                                                       string rightVersion,
                                                       List<DiffEngine.Chunk> diff,
                                                       SourceItemResult result)
                                              {
                                                  callbackLeftPathname = leftPathname;
                                                  callbackResult = result;
                                              });

                Assert.Equal(@"C:\Foo", callbackLeftPathname);
                Assert.Equal(SourceItemResult.E_PathNotFound, callbackResult);
            }
        }

        public class Diff_File
        {
            [Fact]
            public void AddedFile()
            {
                string callbackLeftPathname = null;
                string callbackLeftVersion = null;
                string callbackRightPathname = null;
                string callbackRightVersion = null;
                List<DiffEngine.Chunk> callbackDiff = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "a\r\nb\r\nc");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Add);

                engine.Diff(@"C:\Foo\bar.txt", false, delegate(string leftPathname,
                                                               string leftVersion,
                                                               string rightPathname,
                                                               string rightVersion,
                                                               List<DiffEngine.Chunk> diff,
                                                               SourceItemResult result)
                                                      {
                                                          callbackLeftPathname = leftPathname;
                                                          callbackLeftVersion = leftVersion;
                                                          callbackRightPathname = rightPathname;
                                                          callbackRightVersion = rightVersion;
                                                          callbackDiff = diff;
                                                          callbackResult = result;
                                                      });

                Assert.Equal(@"C:\Foo\bar.txt", callbackLeftPathname);
                Assert.Equal("revision 0", callbackLeftVersion);
                Assert.Equal(@"C:\Foo\bar.txt", callbackRightPathname);
                Assert.Equal("revision 0", callbackRightVersion);
                Assert.Equal(1, callbackDiff.Count);
                TestableTfsEngine.AssertCorrectChunk(callbackDiff[0], DiffEngine.ChunkType.Right, "a", "b", "c");
                Assert.Equal(SourceItemResult.S_Ok, callbackResult);
            }

            [Fact]
            public void ConflictedFile()
            {
                string callbackLeftPathname = null;
                string callbackLeftVersion = null;
                string callbackRightPathname = null;
                string callbackRightVersion = null;
                List<DiffEngine.Chunk> callbackDiff = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "a\r\nb\r\nc");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt.r5", "a\r\nq\r\nc");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);
                engine.TfsState.MarkFileAsConflicted(@"C:\Foo\bar.txt", @"C:\Foo\bar.txt.r5", 5);
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "a\r\ns\r\nc");

                engine.Diff(@"C:\Foo\bar.txt", false, delegate(string leftPathname,
                                                               string leftVersion,
                                                               string rightPathname,
                                                               string rightVersion,
                                                               List<DiffEngine.Chunk> diff,
                                                               SourceItemResult result)
                                                      {
                                                          callbackLeftPathname = leftPathname;
                                                          callbackLeftVersion = leftVersion;
                                                          callbackRightPathname = rightPathname;
                                                          callbackRightVersion = rightVersion;
                                                          callbackDiff = diff;
                                                          callbackResult = result;
                                                      });

                Assert.Equal(@"C:\Foo\bar.txt", callbackLeftPathname);
                Assert.Equal("revision 5", callbackLeftVersion);
                Assert.Equal(@"C:\Foo\bar.txt", callbackRightPathname);
                Assert.Equal("working copy", callbackRightVersion);
                Assert.Equal(4, callbackDiff.Count);
                TestableTfsEngine.AssertCorrectChunk(callbackDiff[0], DiffEngine.ChunkType.Same, "a");
                TestableTfsEngine.AssertCorrectChunk(callbackDiff[1], DiffEngine.ChunkType.Left, "q");
                TestableTfsEngine.AssertCorrectChunk(callbackDiff[2], DiffEngine.ChunkType.Right, "s");
                TestableTfsEngine.AssertCorrectChunk(callbackDiff[3], DiffEngine.ChunkType.Same, "c");
                Assert.Equal(SourceItemResult.S_Ok, callbackResult);
            }

            [Fact]
            public void DeletedFile()
            {
                string callbackLeftPathname = null;
                string callbackLeftVersion = null;
                string callbackRightPathname = null;
                string callbackRightVersion = null;
                List<DiffEngine.Chunk> callbackDiff = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "a\r\nb\r\nc");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Delete);

                engine.Diff(@"C:\Foo\bar.txt", false, delegate(string leftPathname,
                                                               string leftVersion,
                                                               string rightPathname,
                                                               string rightVersion,
                                                               List<DiffEngine.Chunk> diff,
                                                               SourceItemResult result)
                                                      {
                                                          callbackLeftPathname = leftPathname;
                                                          callbackLeftVersion = leftVersion;
                                                          callbackRightPathname = rightPathname;
                                                          callbackRightVersion = rightVersion;
                                                          callbackDiff = diff;
                                                          callbackResult = result;
                                                      });

                Assert.Equal(@"C:\Foo\bar.txt", callbackLeftPathname);
                Assert.Equal("revision 3", callbackLeftVersion);
                Assert.Equal(@"C:\Foo\bar.txt", callbackRightPathname);
                Assert.Equal("working copy", callbackRightVersion);
                Assert.Equal(1, callbackDiff.Count);
                TestableTfsEngine.AssertCorrectChunk(callbackDiff[0], DiffEngine.ChunkType.Left, "a", "b", "c");
                Assert.Equal(SourceItemResult.S_Ok, callbackResult);
            }

            [Fact]
            public void MissingFile()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "a\nb\nc");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);
                engine.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                engine.Diff(@"C:\Foo\bar.txt", false, delegate
                                                      {
                                                          throw new InvalidOperationException("Should not be called back");
                                                      });
            }

            [Fact]
            public void ModifiedFile()
            {
                string callbackLeftPathname = null;
                string callbackLeftVersion = null;
                string callbackRightPathname = null;
                string callbackRightVersion = null;
                List<DiffEngine.Chunk> callbackDiff = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "a\r\nb\r\nc");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "b\r\nc\r\nd e");

                engine.Diff(@"C:\Foo\bar.txt", false, delegate(string leftPathname,
                                                               string leftVersion,
                                                               string rightPathname,
                                                               string rightVersion,
                                                               List<DiffEngine.Chunk> diff,
                                                               SourceItemResult result)
                                                      {
                                                          callbackLeftPathname = leftPathname;
                                                          callbackLeftVersion = leftVersion;
                                                          callbackRightPathname = rightPathname;
                                                          callbackRightVersion = rightVersion;
                                                          callbackDiff = diff;
                                                          callbackResult = result;
                                                      });

                Assert.Equal(@"C:\Foo\bar.txt", callbackLeftPathname);
                Assert.Equal("revision 3", callbackLeftVersion);
                Assert.Equal(@"C:\Foo\bar.txt", callbackRightPathname);
                Assert.Equal("working copy", callbackRightVersion);
                Assert.Equal(3, callbackDiff.Count);
                TestableTfsEngine.AssertCorrectChunk(callbackDiff[0], DiffEngine.ChunkType.Left, "a");
                TestableTfsEngine.AssertCorrectChunk(callbackDiff[1], DiffEngine.ChunkType.Same, "b", "c");
                TestableTfsEngine.AssertCorrectChunk(callbackDiff[2], DiffEngine.ChunkType.Right, "d e");
                Assert.Equal(SourceItemResult.S_Ok, callbackResult);
            }

            [Fact]
            public void NotInAWorkingFolder()
            {
                string callbackLeftPathname = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "text");

                engine.Diff(@"C:\Foo\bar.txt", false, delegate(string leftPathname,
                                                               string leftVersion,
                                                               string rightPathname,
                                                               string rightVersion,
                                                               List<DiffEngine.Chunk> diff,
                                                               SourceItemResult result)
                                                      {
                                                          callbackLeftPathname = leftPathname;
                                                          callbackResult = result;
                                                      });

                Assert.Equal(@"C:\Foo\bar.txt", callbackLeftPathname);
                Assert.Equal(SourceItemResult.E_NotInAWorkingFolder, callbackResult);
            }

            [Fact]
            public void UnmodifiedFile()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "a\nb\nc");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);

                engine.Diff(@"C:\Foo\bar.txt", false, delegate
                                                      {
                                                          throw new InvalidOperationException("Should not be called back");
                                                      });
            }

            [Fact]
            public void UnversionedFile()
            {
                string callbackLeftPathname = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);

                engine.Diff(@"C:\Foo\bar.txt", false, delegate(string leftPathname,
                                                               string leftVersion,
                                                               string rightPathname,
                                                               string rightVersion,
                                                               List<DiffEngine.Chunk> diff,
                                                               SourceItemResult result)
                                                      {
                                                          callbackLeftPathname = leftPathname;
                                                          callbackResult = result;
                                                      });

                Assert.Equal(@"C:\Foo\bar.txt", callbackLeftPathname);
                Assert.Equal(SourceItemResult.E_NotUnderSourceControl, callbackResult);
            }
        }

        public class Diff_Folder
        {
            [Fact]
            public void FolderNotUnderSourceControl()
            {
                string callbackLeftPathname = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);

                engine.Diff(@"C:\Foo\Bar", false, delegate(string leftPathname,
                                                           string leftVersion,
                                                           string rightPathname,
                                                           string rightVersion,
                                                           List<DiffEngine.Chunk> diff,
                                                           SourceItemResult result)
                                                  {
                                                      callbackLeftPathname = leftPathname;
                                                      callbackResult = result;
                                                  });

                Assert.Equal(@"C:\Foo\Bar", callbackLeftPathname);
                Assert.Equal(SourceItemResult.E_NotUnderSourceControl, callbackResult);
            }

            [Fact]
            public void MissingFolder()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Bar", @"C:\Foo\Bar", 2, 2, SourceItemStatus.Unmodified);
                engine.FileSystem.DeleteDirectory(@"C:\Foo\Bar", true);

                engine.Diff(@"C:\Foo", true, delegate
                                             {
                                                 throw new InvalidOperationException("Should not be called back");
                                             });
            }

            [Fact]
            public void NonRecursiveFileToDiff()
            {
                List<string> callbackFiles = new List<string>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Hi there");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Hello there");

                engine.Diff(@"C:\Foo", false, delegate(string leftPathname,
                                                       string leftVersion,
                                                       string rightPathname,
                                                       string rightVersion,
                                                       List<DiffEngine.Chunk> diff,
                                                       SourceItemResult result)
                                              {
                                                  callbackFiles.Add(leftPathname);
                                              });

                Assert.Contains(@"C:\Foo\bar.txt", callbackFiles);
            }

            [Fact]
            public void NonRecursiveFileToDiffInSubFolder()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\bar.txt", "Hi there");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("httP://tfsUrl", "$/Foo/Bar", @"C:\Foo\Bar", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\Bar\bar.txt", 2, 3, SourceItemStatus.Unmodified);
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\bar.txt", "Hello there");

                engine.Diff(@"C:\Foo", false, delegate
                                              {
                                                  throw new InvalidOperationException("Should not be called back");
                                              });
            }

            [Fact]
            public void NonRecursiveNothingToDiff()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Hi there");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);

                engine.Diff(@"C:\Foo", false, delegate
                                              {
                                                  throw new InvalidOperationException("Should not be called back");
                                              });
            }

            [Fact]
            public void Recursive()
            {
                List<string> callbackFiles = new List<string>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\bar.txt", "Hi there");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("httP://tfsUrl", "$/Foo/Bar", @"C:\Foo\Bar", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\Bar\bar.txt", 2, 3, SourceItemStatus.Unmodified);
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\bar.txt", "Hello there");

                engine.Diff(@"C:\Foo", true, delegate(string leftPathname,
                                                      string leftVersion,
                                                      string rightPathname,
                                                      string rightVersion,
                                                      List<DiffEngine.Chunk> diff,
                                                      SourceItemResult result)
                                             {
                                                 callbackFiles.Add(leftPathname);
                                             });

                Assert.Contains(@"C:\Foo\Bar\bar.txt", callbackFiles);
            }
        }
    }
}